package com.hzlj.position.config.rest.wd.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fz.common.base.core.JsonUtils;
import com.fz.common.base.enums.CoordinateType;
import com.fz.common.base.exception.PubException;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hzlj.position.config.common.dto.common.PositionAddDTO;
import com.hzlj.position.config.common.dto.common.PositionAddExtDTO;
import com.hzlj.position.config.common.dto.wdConfig.WdConfigQueryResultDTO;
import com.hzlj.position.config.common.enums.WarningType;
import com.hzlj.position.config.rest.LonLatAddressRest;
import com.hzlj.position.config.rest.wd.WdLocateRest;
import com.hzlj.position.config.rest.wd.impl.traxBean.TraxBeanAlarmMessageResultDTO;
import com.hzlj.position.config.rest.wd.impl.traxBean.TraxBeanLocateResultDTO;
import com.hzlj.position.config.rest.wd.impl.traxBean.TraxBeanTokenResultDTO;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.TimeZones;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.BiFunction;

/**
 * 辽宁-traxBean
 **/
@Slf4j
@Service("traxBeanLocate")
public class TraxBeanLocate implements WdLocateRest {
    //获取token
    private final static String TOKEN_URI = "/api/token/get_token?appId=%s&password=%s&timestamp=%s";
    //定位
    private final static String LOCATION_URI = "/api/location/get_location_info?imei=%s&MapType=%s";
    //发送消息
    private final static String SEND_TEXT_URI = "/api/Command/sendText?imei=%s&params=%s";
    //发送命令
    private final static String SEND_CMD_URI = "/api/Command/sendCommand?imei=%s&cmdCode=%s&params=%s";
    //报警列表
    private final static String ALARM_MESSAGE_URI = "/api/alarm/get_alarm_messageByAppId?appId=%s&mapType=%s&beginTime=%s&endTime=%s";
    /**
     * tokens
     */
    private final static Map<String, TraxBeanTokenResultDTO> tokens = Maps.newConcurrentMap();
    private final static String mapType = "Baidu";
    @Resource
    private LonLatAddressRest lonLatAddressRest;


    @Override
    public PositionAddDTO fetchPosition(WdConfigQueryResultDTO config,
                                        String jgCode,
                                        String imei,
                                        BiFunction<String, String, String> dictFun) {
        TraxBeanLocateResultDTO locate = this.locate(TraxBeanConfig.of(config), imei);
        //位置
        PositionAddDTO position = defaultPosition(
                imei,
                locate.getLng(),
                locate.getLat(),
                locate.getSignalTime(),
                CoordinateType.BD09,
                config);
        //设置位置信息
        position.setPosition(
                lonLatAddressRest.getAddress(position.getLongitude(), position.getLatitude())
        );
        position.setPositionExt(new PositionAddExtDTO().setElectricValue(locate.getBattery().intValue()));

        return position;
    }

    @Override
    public List<PositionAddDTO> fetchAlarmsPositions(WdConfigQueryResultDTO config,
                                                     String jgCode,
                                                     Date startTime,
                                                     Date endTime,
                                                     BiFunction<String, String, String> dictFun) {
        TraxBeanConfig traxBeanConfig = TraxBeanConfig.of(config);
        List<TraxBeanAlarmMessageResultDTO> alarms = this.getAlarms(traxBeanConfig, DateUtil.formatDateTime(startTime), DateUtil.formatDateTime(endTime));
        if (ObjectUtil.isEmpty(alarms)) {
            return null;
        }

        List<PositionAddDTO> positions = Lists.newArrayList();
        for (TraxBeanAlarmMessageResultDTO alarm : alarms) {
            WarningType warningType;
            switch (alarm.getAlarmType()) {
                case "3":
                    warningType = WarningType.LOWER_POWER;
                    break;
                case "300":
                    warningType = WarningType.DEMOUNT;
                    break;
                default:
                    continue;
            }
            //位置
            PositionAddDTO position = defaultPosition(
                    alarm.getImei(),
                    alarm.getLng(),
                    alarm.getLat(),
                    alarm.getAlarmTime(),
                    CoordinateType.BD09,
                    config);
            //报警信息
            position.setWarningType(warningType.getCode());
            //电量信息
            TraxBeanLocateResultDTO locate = this.locate(traxBeanConfig, alarm.getImei());
            if (locate != null && locate.getBattery() != null) {
                position.setPositionExt(new PositionAddExtDTO().setElectricValue(locate.getBattery().intValue()));
            } else {
                log.warn("腕带定位没有获取到当前电量信息:alarm={},locate={}", alarm, locate);
            }

            positions.add(position);
        }
        return positions;
    }

    @Override
    public void updateLocateInterval(WdConfigQueryResultDTO config,
                                     String jgCode,
                                     String imei,
                                     Integer locateInterval) {
        this.sendCmd(TraxBeanConfig.of(config), imei, "0003", locateInterval + "");
    }

    @Override
    public void shutdown(WdConfigQueryResultDTO config,
                         String jgCode,
                         String imei) {
        this.sendCmd(TraxBeanConfig.of(config), imei, "0048", "0048");
    }

    @Override
    public void reboot(WdConfigQueryResultDTO config,
                       String jgCode,
                       String imei) {
        this.sendCmd(TraxBeanConfig.of(config), imei, "0010", "0010");
    }

    public void sendText(WdConfigQueryResultDTO config,
                         String jgCode,
                         String imei,
                         String text) {
        this.sendText(TraxBeanConfig.of(config), imei, text);
    }

    @Override
    public String wdName() {
        return "traxBean";
    }

    /**
     * 登录
     */
    public void token(TraxBeanConfig config) {
        String timestamp = (System.currentTimeMillis() / 1000) + "";
        String url = String.format(TOKEN_URI,
                config.getAppId(),
                MD5.create().digestHex(config.getAppKey() + config.getAppId() + timestamp),
                timestamp
        );
        TraxBeanTokenResultDTO token = execute(config, url, "获取token", TraxBeanTokenResultDTO.class);
        tokens.put(config.getAppId(), token);
    }


    /**
     * 定位
     * TODO：这里获取的不是实时位置，要等两分钟之后才会有位置（等待GPS打开之后，所以需要过一两分钟后再次获取位置）
     */
    public TraxBeanLocateResultDTO locate(TraxBeanConfig config, String imei) {
        String url = String.format(LOCATION_URI,
                imei,
                mapType
        );
        TraxBeanLocateResultDTO locateResult
                = execute(config, url, "获取定位", TraxBeanLocateResultDTO.class);
        if (ObjectUtil.isEmpty(locateResult)) {
            return null;
        }
        return locateResult;
    }

    /**
     * 定位
     */
    public TraxBeanLocateResultDTO locateSync(TraxBeanConfig config, String imei, Date now) {
        TraxBeanLocateResultDTO locate = locate(config, imei);
        if (DateUtil.parseDateTime(locate.getSignalTime()).compareTo(now) >= 0) {
            return locate;
        }
        return locateSync(config, imei, now);
    }


    /**
     * 发送消息
     */
    public void sendText(TraxBeanConfig config, String imei, String msg) {
        //发送消息
        String url = String.format(SEND_TEXT_URI,
                imei,
                msg
        );
        execute(config, url, "发送消息", TraxBeanEmptyResult.class);
    }


    /**
     * 关机：0048
     * 重启：0010
     * 定位间隔：0003 秒
     */
    public void sendCmd(TraxBeanConfig config, String imei, String cmd, String params) {
        String url = String.format(SEND_CMD_URI,
                imei,
                cmd,
                params
        );
        execute(config, url, "发送命令", TraxBeanEmptyResult.class);
    }

    /**
     * 获取报警列表
     * 低电 3
     * sos 05
     * 离线 14
     * 拆除300
     * 进电子围栏报警 1
     * 出电子围栏报警 2
     */
    public List<TraxBeanAlarmMessageResultDTO> getAlarms(TraxBeanConfig config, String beginTime, String endTime) {
        // UTC0时间
        TimeZone newYorkTimeZone = TimeZone.getTimeZone(TimeZones.GMT_ID);
        DateFormat dateFormat = new SimpleDateFormat(DatePattern.NORM_DATETIME_PATTERN);
        dateFormat.setTimeZone(newYorkTimeZone);
        beginTime = dateFormat.format(DateUtil.parseDateTime(beginTime));
        endTime = dateFormat.format(DateUtil.parseDateTime(endTime));

        String url = String.format(ALARM_MESSAGE_URI,
                config.getAppId(),
                mapType,
                beginTime,
                endTime
        );
        try {
            return executeList(config, url, "报警消息列表", TraxBeanAlarmMessageResultDTO.class);
        } catch (PubException e) {
            log.debug("未获取到报警消息列表:config={},msg={}", config, e.getMsg());
        } catch (Exception e) {
            log.error("获取报警消息列表失败:config={}", config, e);
        }
        return Lists.newArrayList();
    }


    private <T> List<T> executeList(TraxBeanConfig config,
                                    String uri,
                                    String buss,
                                    Class<T> clazz) {
        return executeList(config, uri, buss, clazz, 0);
    }

    @SuppressWarnings("unchecked")
    private <T> List<T> executeList(TraxBeanConfig config,
                                    String uri,
                                    String buss,
                                    Class<T> clazz,
                                    Integer retryCount) {
        log.debug("【TraxBean】请求参数({})：url={}", buss, uri);
        String accessTokenUrl = uri;
        if (tokens.containsKey(config.getAppId())) {
            accessTokenUrl += "&accessToken=" + tokens.get(config.getAppId()).getAccessToken();
        }
        HttpRequest request = HttpUtil.createGet(config.getUrl() + accessTokenUrl);
        try (HttpResponse execute = request.execute()) {
            String body = execute.body();
            TraxBeanListResult<T> result = JsonUtils.json2obj(body, TraxBeanListResult.class, clazz);
            log.debug("【TraxBean】请求结果({})：url={},result={}", buss, uri, body);
            if (result.success()) {
                return result.result;
            } else if (needLogin(result, retryCount)) {
                retryCount++;
                token(config);
                return executeList(config, uri, buss, clazz, retryCount);
            }
            throw new PubException(result.getMsg());
        } catch (PubException e) {
            throw e;
        } catch (Exception e) {
            log.error("【TraxBean】发起请求失败：url={}", uri, e);
            throw new PubException(String.format("【TraxBean】发起请求失败(%s)", buss));
        }
    }

    private <T> T execute(TraxBeanConfig config,
                          String uri,
                          String buss,
                          Class<T> clazz) {
        return execute(config, uri, buss, clazz, 0);
    }

    @SuppressWarnings("unchecked")
    private <T> T execute(TraxBeanConfig config,
                          String uri,
                          String buss,
                          Class<T> clazz,
                          Integer retryCount) {
        log.info("【TraxBean】请求参数({})：url={}", buss, uri);
        String accessTokenUrl = uri;
        if (tokens.containsKey(config.getAppId())) {
            accessTokenUrl += "&accessToken=" + tokens.get(config.getAppId()).getAccessToken();
        }
        HttpRequest request = HttpUtil.createGet(config.getUrl() + accessTokenUrl);
        try (HttpResponse execute = request.execute()) {
            String body = execute.body();
            TraxBeanResult<T> result = JsonUtils.json2obj(body, TraxBeanResult.class, clazz);
            log.info("【TraxBean】请求结果({})：url={},result={}", buss, uri, body);
            if (result.success()) {
                return result.result;
            } else if (needLogin(result, retryCount)) {
                retryCount++;
                token(config);
                return execute(config, uri, buss, clazz, retryCount);
            }
            throw new PubException(result.getMsg());
        } catch (PubException e) {
            throw e;
        } catch (Exception e) {
            log.error("【TraxBean】发起请求失败：url={}", uri, e);
            throw new PubException(String.format("【TraxBean】发起请求失败(%s)", buss));
        }
    }


    private boolean needLogin(TraxBeanCommonResult result, Integer retryCount) {
        if (retryCount >= 1) {
            return false;
        }
        if (result.getState().equals(403)) {
            return true;
        }
        return false;
    }

    @Data
    public static class TraxBeanListResult<T> extends TraxBeanCommonResult {
        private List<T> result;
    }

    @Data
    public static class TraxBeanResult<T> extends TraxBeanCommonResult {
        @JsonProperty("Result")
        private T result;
    }


    public static class TraxBeanEmptyResult extends TraxBeanCommonResult {

    }

    @Data
    public static class TraxBeanCommonResult {
        @JsonProperty("State")
        private Integer state;
        @JsonProperty("Msg")
        private String msg;

        public boolean success() {
            return state.equals(0);
        }
    }

    @Data
    public static class TraxBeanConfig {
        private String url = "https://napi.amber360.com";
        private String appId = "1241";
        private String appKey = "F05D594F-1E6B-493B-AF7D-D91D15ED114F";
        private String username = "LiaoningUnicom";
        private String password = "20240311";
        private String mapType = "Baidu";
        /**
         * 默认获取10分钟之内的数据
         */
        private Integer minute = 10;

        public static TraxBeanConfig of(WdConfigQueryResultDTO config) {
            TraxBeanConfig traxBeanConfig = JsonUtils.json2obj(config.getRequestConfig(), TraxBeanConfig.class);
            traxBeanConfig.setUrl(config.getRequestUrl());
            return traxBeanConfig;
        }
    }

}
